package com.andong.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.andong.annotations.AccessControl;
import com.andong.domain.pojo.Result;
import com.andong.domain.vo.AdminAuthVO;
import com.andong.service.AdminAuthService;
import com.andong.utils.BaseContext;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import static com.andong.utils.SystemConstant.*;

@Slf4j
@Component
public class AuthCheckInterceptor implements HandlerInterceptor {
    @Resource
    private RedisTemplate<String, List<String>> redisTemplate;
    @Resource
    private AdminAuthService adminAuthService;
    @Override // 目标资源方法运行前运行, 返回true: 放行, 放回false, 不放行
    public boolean preHandle(@NotNull HttpServletRequest req, @NotNull HttpServletResponse resp, @NotNull Object handler) throws Exception {
        log.info("开始进行权限拦截校验");
        // 1.从当前线程中获取访客身份和ID
        List<Object> idAndIdentity = BaseContext.getCurrentIdAndIdentity();
        String identity = (String) idAndIdentity.get(1); // 访客身份
        Long id = (Long) idAndIdentity.get(0); // 访客id
        log.info("当前访客身份是：{}", identity);
        log.info("当前访客id是：{}", id);
        // 2.获取当前请求所访问的方法
        if (handler instanceof HandlerMethod handlerMethod) {
            String controllerName = handlerMethod.getBeanType().getSimpleName();
            String methodName = handlerMethod.getMethod().getName();
            log.info("当前访客访问的方法是：{}.{}", controllerName, methodName);
            // 获取指定类型的注解
            AccessControl accessControl = handlerMethod.getMethodAnnotation(AccessControl.class);
            if (accessControl == null) {
                log.info("没有注解");
                return true; // 说明没有限制权限
            }
            // 处理获取到的注解信息
            String[] roles = accessControl.roles();
            String permission = accessControl.permission();
            List<String> rolesList = Arrays.asList(roles);
            if (!rolesList.contains(identity)) {
                // 如果该身份没有访问权限则返回错误信息
                Result error = Result.error(NO_AUTH);
                // 手动转换 对象--json --------> 阿里巴巴fastJSON
                String noAuth = JSONObject.toJSONString(error);
                resp.getWriter().write(noAuth);
                log.info("您的{}没有访问该方法的权利！", identity);
                return false;
            }
            if (!ADMIN.equals(identity)) {
                log.info("不是管理员直接放行");
                return true; // 不是普通管理员的身份则直接放行
                /* 直接放行的原因:
                 *  1. 前面已经校验过是否该方法能被该身份访问 结果是能
                 *  2. 只要注解包含该身份(student, coach, super_admin)，说明他一定能操作该方法
                 *  3. 但是每个普通管理员的职责是不同的, 因此只需要对普通管理员的权限进行细分。
                 */
            }
            if (permission.isEmpty()) {
                log.info("只需要身份，不需要具体权限，直接放行");
                return true; // 表示这个方法只需要身份，不需要具体权限
            }
            // 从redis缓存获取该管理员的权限信息
            List<String> auths = redisTemplate.opsForValue().get(ADMIN_AUTH + id);
            log.info(String.valueOf(auths));
            if (Objects.isNull(auths)) {
                log.info("redis缓存中不存在！");
                auths = new ArrayList<>();
                // 前往数据库查询
                List<AdminAuthVO> adminAuthVOList = adminAuthService.getAuthById(id);
                for (AdminAuthVO adminAuthVO : adminAuthVOList) {
                    auths.add(adminAuthVO.getName());
                }
            }
            if (!auths.contains(permission)) {
                log.info("没有具体权限信息！");
                Result error = Result.error(NO_AUTH);
                // 手动转换 对象--json --------> 阿里巴巴fastJSON
                String noAuth = JSONObject.toJSONString(error);
                resp.getWriter().write(noAuth);
                return false;
            }
        }
        return true;
    }
}
